using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using PentaBiz.Interop;

namespace PentaBiz.Testing.Interop
{
    [TestClass]
    public class UnitTestMarshalExtensions
    {
        [TestMethod]
        [TestCategory("MarshalExtensions")]
        public void TestMarshalFixedLengthString()
        {
            var s = new FixedLengthString() { FixedString = "Hello world" };
            var size = Marshal.SizeOf(s);

            Assert.AreEqual(30, size);

            var b = s.MarshalToByteArray();

            Assert.AreEqual(size, b.Length, "Marshalled size must be the same");
            var s2 = b.MarshalToStruct<FixedLengthString>();

            Assert.AreEqual(s, s2);
            Assert.AreEqual(s.FixedString, s2.FixedString);
            Assert.AreEqual("Hello world", s2.FixedString);


        }



        [TestMethod]
        [TestCategory("MarshalExtensions")]
        public void TestMarshalComplexStruct()
        {
            var size = 0;
            var b = MarshalComplexData(out size);


            Assert.AreEqual(size, b.Length);
            var c2 = b.MarshalToStruct<ComplexStruct>();

            Assert.IsNotNull(c2.DataArray);
        }

        [TestMethod]
        [TestCategory("MarshalExtensions")]
        public void TestMarshalOneDimensionArray()
        {
            var v = new double[10];
            InitializeArray(v);

            var b = v.MarshalToByteArray();

            var expectedSize = 10 * Marshal.SizeOf(typeof(double));
            Assert.AreEqual(expectedSize, b.Length);


            var v2 = b.ReadOneDimensionDoubleArray(10);

            Assert.IsNotNull(v2);
            Assert.AreEqual(10, v2.Length);
            Assert.AreEqual(0, v2[0]);
            Assert.AreEqual(1, v2[1]);
            Assert.AreEqual(2, v2[2]);
        }

        [TestMethod]
        [TestCategory("MarshalExtensions")]
        public void TestMarshalTwoDimensionArray()
        {
            var v = new double[10, 10];
            InitializeArray(v);

            var b = v.MarshalToByteArray();

            var expectedSize = 10 * 10 * Marshal.SizeOf(typeof(double));
            Assert.AreEqual(expectedSize, b.Length);


            var v2 = b.ReadTwoDimensionDoubleArray(10, 10);

            Assert.IsNotNull(v2);
            Assert.AreEqual(10 * 10, v2.Length);
            Assert.AreEqual(0, v2[0, 0]);
            Assert.AreEqual(1, v2[0, 1]);
            Assert.AreEqual(2, v2[0, 2]);
            Assert.AreEqual(10, v2[1, 0]);
            Assert.AreEqual(11, v2[1, 1]);
            Assert.AreEqual(12, v2[1, 2]);
        }

        [TestMethod]
        [TestCategory("MarshalExtensions")]
        public void TestMarshalThreeDimensionArray()
        {
            var v = new double[10, 10, 10];
            InitializeArray(v);

            var b = v.MarshalToByteArray();

            var expectedSize = 10 * 10 * 10 * Marshal.SizeOf(typeof(double));
            Assert.AreEqual(expectedSize, b.Length);


            var v2 = b.ReadThreeDimensionDoubleArray(10, 10, 10);

            Assert.IsNotNull(v2);
            Assert.AreEqual(10 * 10 * 10, v2.Length);
            Assert.AreEqual(0, v2[0, 0, 0]);
            Assert.AreEqual(1, v2[0, 0, 1]);
            Assert.AreEqual(2, v2[0, 0, 2]);
            Assert.AreEqual(10, v2[0, 1, 0]);
            Assert.AreEqual(11, v2[0, 1, 1]);
            Assert.AreEqual(12, v2[0, 1, 2]);
        }

        [TestMethod]
        [TestCategory("MarshalExtensions")]
        public void TestMarshalSingleDimension()
        {
            var d = new double[3, 10];
            InitializeArray(d);

            var value = d.MarshalSingleDimension(1);

            Assert.IsNotNull(value);
            Assert.AreEqual(10, value[0]);
            Assert.AreEqual(11, value[1]);

            value = d.MarshalSingleDimension(0);

            Assert.IsNotNull(value);
            Assert.AreEqual(0, value[0]);
            Assert.AreEqual(1, value[1]);

            value = d.MarshalSingleDimension(2);

            Assert.IsNotNull(value);
            Assert.AreEqual(20, value[0]);
            Assert.AreEqual(21, value[1]);
        }

        [TestMethod]
        [TestCategory("MarshalExtensions")]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void TestMarshalSingleDimensionArgException1()
        {
            var d = new double[3, 10];
            InitializeArray(d);

            var value = d.MarshalSingleDimension(-1);

        }

        [TestMethod]
        [TestCategory("MarshalExtensions")]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void TestMarshalSingleDimensionArgException2()
        {
            var d = new double[3, 10];
            InitializeArray(d);

            var value = d.MarshalSingleDimension(3);

        }

       

        public void InitializeArray(double[,] array)
        {
            double v = 0;
            for (int i = 0; i < array.GetLength(0); i++)
                for (int j = 0; j < array.GetLength(1); j++, v++)
                    array[i, j] = v;


        }
        public void InitializeArray(double[, ,] array)
        {
            double v = 0;
            for (int i = 0; i < array.GetLength(0); i++)
                for (int j = 0; j < array.GetLength(1); j++)
                    for (int k = 0; k < array.GetLength(2); k++, v++)
                        array[i, j, k] = v;


        }
        public void InitializeArray(double[] array)
        {
            double v = 0;
            for (int i = 0; i < array.GetLength(0); i++, v++)
                array[i] = v;


        }

        byte[] MarshalComplexData(out int size)
        {
            var c = new ComplexStruct()
                        {
                            Id = Guid.NewGuid(),
                            DataArray = ComplexStruct.InitializeDataArray(),
                        };

            var b = c.MarshalToByteArray();

            size = Marshal.SizeOf(c);

            return b;
        }
        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        struct FixedLengthString
        {
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 30)]
            public string FixedString;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        struct VariableLengthString
        {
            //#region [MarshalAs(UnmanagedType.BStr)]
            //public int VariableStringSize;
            //[MarshalAs(UnmanagedType.BStr, SizeParamIndex = 0)] //size:4 (seems like a pointer)
            //public string VariableString;
            //#endregion

            //#region [MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)]
            //public int VariableStringSize;

            //[MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 0)]
            //public IntPtr VariableString;
            //#endregion
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        struct ComplexStruct
        {
            public Guid Id;

            [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_R8)]
            public double[,] DataArray;

            public static double[,] InitializeDataArray()
            {
                var d = new double[3, 1000];

                var rnd = new Random();

                for (int i = 0; i < 3; i++)
                    for (int j = 0; j < 1000; j++)
                        d.SetValue(rnd.NextDouble(), i, j);

                return d;
            }
        }


    }
}